package com.wsoft.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.wsoft.constant.RedisKeyConstant;
import com.wsoft.core.annotation.GetRedisCache;
import com.wsoft.core.annotation.InitRedisCache;
import com.wsoft.core.exception.Asserts;
import com.wsoft.system.dto.SysMenuDto;
import com.wsoft.system.dto.SysMenuNode;
import com.wsoft.system.entity.SysMenuEntity;
import com.wsoft.system.manager.MenuManager;
import com.wsoft.system.mapper.SysMenuMapper;
import com.wsoft.system.service.ISysMenuService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单表 服务实现类
 * </p>
 *
 * @author kyle
 * @since 2024年09月02日 09:04:57
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenuEntity> implements ISysMenuService {

    @Resource
    private MenuManager menuManager;

    @Override
    @InitRedisCache(types = {RedisKeyConstant.MENU_TREE_ALL},
            values = {RedisKeyConstant.REDIS_NULL})
    public boolean insert(SysMenuDto dto) {
        SysMenuEntity entity = Convert.convert(SysMenuEntity.class, dto);
        List<SysMenuEntity> list = menuManager.getMenuByTitle(dto.getTitle());
        if (CollUtil.isNotEmpty(list)) {
            Asserts.fail("[sys.menu.title.exist]");
        }
        if (dto.getPaId() == null) {
            entity.setPaId(0L);
        }
        return save(entity);
    }

    @Override
    @InitRedisCache(
            types = {RedisKeyConstant.MENU_CACHE, RedisKeyConstant.MENU_TREE_ALL, RedisKeyConstant.ROLE_MENU_CACHE + "*"},
            values = {"id", RedisKeyConstant.REDIS_NULL, RedisKeyConstant.REDIS_PATTERN}
    )
    public boolean update(SysMenuDto dto) {
        SysMenuEntity entity = Convert.convert(SysMenuEntity.class, dto);
        QueryWrapper<SysMenuEntity> query = new QueryWrapper<>();
        query.lambda().eq(SysMenuEntity::getTitle, entity.getTitle())
                .ne(SysMenuEntity::getId, entity.getId());
        List<SysMenuEntity> list = list(query);
        if (CollUtil.isNotEmpty(list)) {
            Asserts.fail("[sys.menu.title.exist]");
        }
        return updateById(entity);
    }

    @Override
    @GetRedisCache(type = RedisKeyConstant.MENU_CACHE,
            value = RedisKeyConstant.REDIS_SELF)
    public SysMenuEntity getMenu(Long id) {
        return getById(id);
    }

    @Override
    @InitRedisCache(
            types = {RedisKeyConstant.MENU_CACHE, RedisKeyConstant.MENU_TREE_ALL, RedisKeyConstant.ROLE_MENU_CACHE + "*"},
            values = {RedisKeyConstant.REDIS_SELF, RedisKeyConstant.REDIS_NULL, RedisKeyConstant.REDIS_PATTERN}
    )
    public boolean delete(Long id) {
        QueryWrapper<SysMenuEntity> query = new QueryWrapper<>();
        query.lambda().eq(SysMenuEntity::getPaId, id);
        List<SysMenuEntity> list = list(query);
        if (CollUtil.isNotEmpty(list)) {
            Asserts.fail("[sys.menu.del.sub]");
        }
        return removeById(id);
    }

    @Override
    public PageInfo<SysMenuEntity> pageMenu(SysMenuDto dto) {
        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        QueryWrapper<SysMenuEntity> query = new QueryWrapper<>();
        query.lambda().eq(SysMenuEntity::getPaId, dto.getPaId());
        return PageInfo.of(list(query));
    }

    @Override
    @GetRedisCache(type = RedisKeyConstant.MENU_TREE_ALL,
            value = RedisKeyConstant.REDIS_NULL)
    public List<SysMenuNode> treeList() {
        QueryWrapper<SysMenuEntity> query = new QueryWrapper<>();
        query.lambda().orderByAsc(SysMenuEntity::getSort, SysMenuEntity::getTitle);
        List<SysMenuEntity> list = list(query);
        return list.stream()
                .filter(menu -> menu.getPaId().equals(0L))
                .map(menu -> covertMenuNode(menu, list)).collect(Collectors.toList());
    }

    @Override
    public List<SysMenuNode> treeList(List<SysMenuEntity> menuList) {
        // 降序排序
        if (CollUtil.isNotEmpty(menuList)) {
            menuList = menuList.stream()
                    // 去重复
                    .collect(Collectors.toMap(SysMenuEntity::getId, p -> p, (p1, p2) -> p1))
                    .values()
                    .stream()
                    // sort升序
                    .sorted(Comparator.comparingInt(SysMenuEntity::getSort))
                    .collect(Collectors.toList());
        }
        List<SysMenuEntity> menuEntities = menuList;

        return menuList.stream()
                .filter(menu -> menu.getPaId().equals(0L))
                .map(menu -> covertMenuNode(menu, menuEntities)).collect(Collectors.toList());
    }


    private SysMenuNode covertMenuNode(SysMenuEntity menu, List<SysMenuEntity> menuList) {
        SysMenuNode node = new SysMenuNode();
        BeanUtils.copyProperties(menu, node);
        List<SysMenuNode> children = menuList.stream()
                .filter(subMenu -> subMenu.getPaId().equals(menu.getId()))
                .map(subMenu -> covertMenuNode(subMenu, menuList)).collect(Collectors.toList());
        node.setChildren(children);
        return node;
    }
}
